import json
import os
import re
import shutil
import sys
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from robot import run_cli
from api_test import testdb
from api_test.common import const
from api_test.models import TestPlanCase, ApiInfo, ApiCaseInfo, TestPlan, GameInfo, Project, TestPlanConf
from api_test.testdb.test_model import TestModel
from api_test.testdb.test_plan_case import TestPlanCase as DbPlanCase


class Run(object):

    def __init__(self, plan_list):
        self.plan_list = plan_list
        path = os.path.join(os.path.split(os.path.dirname(__file__))[0])
        self.suite_path = os.path.join(path, 'suites')
        self.report_path = os.path.join(path, 'reports')
        self.init_data()  # 初始化数据
        self.name = None
        const.add_service()

    def generate_report_dir(self, plan_id):
        self.name = TestPlan.objects.get(id=plan_id).name.lower()
        path = os.path.join(self.report_path, self.name)
        shutil.rmtree(self.report_path, ignore_errors=True)
        os.makedirs(path, exist_ok=True)

    def generate_suite_dir(self):
        sys.path.append(os.path.abspath(os.path.curdir))
        shutil.rmtree(self.suite_path, ignore_errors=True)
        os.mkdir(self.suite_path)
        if not os.path.isdir(self.suite_path):
            os.mkdir(self.suite_path)

    @staticmethod
    def init_test_data(plan_id):
        config = TestPlanConf.objects.select_related().filter(plan_id=plan_id)
        plan_name = TestPlan.objects.get(id=plan_id).name
        env = json.loads(config[0].env) if config else {}
        case_list = TestPlanCase.objects.filter(plan_id=plan_id)
        game_id = Project.objects.get(id=TestPlan.objects.get(id=plan_id).project_id).game_id
        server = GameInfo.objects.get(id=game_id).name
        test_plan_case = testdb.TestPlanCase()
        request_list = []
        for case in case_list:
            api = ApiInfo.objects.get(id=ApiCaseInfo.objects.get(id=case.case_id).api_id)
            request = json.loads(case.request)
            if env:
                if case.env:
                    request['url'] = f'{api.protocol}://{case.env}{request["url"]}'
                else:
                    host = env.get('websocket') if api.protocol in ['ws', 'wss'] else env.get('http')
                    request['url'] = f'{api.protocol}://{host}{request["url"]}'
            else:
                request['url'] = f'{api.protocol}://{case.env}{request["url"]}' if case.env else request['url']
            request_list.append(dict(service=server, api_name=f'TestPlan{plan_id}_Case{case.index}',
                                     http_type='post', protocol=api.protocol, req_json=json.dumps(request)))
        test_plan_case.add_all(request_list)

    def init_data(self):
        TestModel().create_all_db()
        DbPlanCase().delete_all_data()
        self.generate_suite_dir()
        for plan_id in self.plan_list:
            self.init_test_data(plan_id)
        with ThreadPoolExecutor(max_workers=len(self.plan_list)) as executor:
            executor.map(self.generate_report_dir, self.plan_list)

    def run_case(self, name):
        args_list = [
                        '-r', f'{self.report_path}/{name}/report.html',
                        '-o', f'{self.report_path}/{name}/output.xml',
                        '-l', f'{self.report_path}/{name}/log.html',
                        '-L', 'TRACE'] + [os.path.join(self.suite_path, f'{name}.robot')]
        run_cli(args_list, False)

    def write_report_to_db(self, start, end, plan_id, name):
        report = ''
        path = os.path.join(self.report_path, f'{name}', 'log.html')
        with open(path, 'r', encoding='utf-8') as f:
            contents = f.readlines()
            for content in contents:
                report += content
                if content.startswith('window.output["stats"]'):
                    i = 0
                    for match in re.findall(r'"fail":\d*', content):
                        i += int(match.split(":")[-1])
        state = 'FAIL' if i != 0 else 'PASS'
        testdb.TestPlanReports().add(plan_id, report=report, start=start, end=end, state=state)

    def main(self):
        start_time = datetime.now()
        for plan_id in self.plan_list:
            name = TestPlan.objects.get(id=plan_id).name.lower()
            self.run_case(name)
            if os.path.exists(os.path.join(self.report_path, f'{name}', 'log.html')):
                self.write_report_to_db(start_time, datetime.now(), plan_id, name)
